#pragma once

#include "cuda_runtime.h"
#include "device_launch_parameters.h"

#include <iostream>

#define __staticinline__ __forceinline static

void printDevProp(cudaDeviceProp devProp);
void cudaDeviceQuery();

// Print device properties
void printDevProp(cudaDeviceProp devProp);

#define PRINT_CURRENT_STATE			\
	fprintf(stderr, "Function : %s\nFile name : %s, Line : %d\n", __FUNCSIG__, __FILE__, __LINE__)

#define CUDA_HANDLE_ERROR(error, message)						\
	if (error != cudaSuccess) {									\
		PRINT_CURRENT_STATE;									\
		fprintf(stderr, "ERROR: %s : %i\n", message, error);	\
		exit(-1);												\
	}															\

// cuda safe memory control macros
#define CUDA_MALLOC(ptr, size) CUDA_HANDLE_ERROR(cudaMalloc((void**)&ptr, size), "cudaMalloc")
#define CUDA_MEMCPY(dst, src, size, kind) CUDA_HANDLE_ERROR(cudaMemcpy(dst, src, size, kind), "cudaMemcpy")
#define CUDA_MEMSET(ptr, value, size) CUDA_HANDLE_ERROR(cudaMemset(ptr, value, size), "cudaMemset")
#define CUDA_FREE(ptr) CUDA_HANDLE_ERROR(cudaFree(ptr), "cudaFree")

// in kernel, KERNEL_INDEX is kernel index integer.
// ex) int idx = KERNEL_INDEX;
#define KERNEL_INDEX blockIdx.x * blockDim.x + threadIdx.x

// KERNEL_CALL_DECLEAR and KERNEL_CALL is used in calling kernel function
// ex)
// KERNEL_CALL_DECELAR(size);
// kernel_function<<<KERNEL_CALL>>>(parameters);
#define KERNEL_CALL_DECLEAR(numElements) 	\
	int threadsPerBlock = 256; \
	int blocksPerGrid = (numElements + threadsPerBlock - 1) / threadsPerBlock;

#define KERNEL_CALL blocksPerGrid, threadsPerBlock


